﻿/*
 * Copyright © 2020, Wuxi Safover Intelligent Technology Co., Ltd. All rights reserved.
 * 
 * 无锡安永智能科技有限公司版权所有，未经许可，不得用于商业用途
 * 
 * Visit: https://www.safover.com/ 
 */

using Ac.Bly;
using Ec.Bewis;
using GPS.BeiTian;
using log4net;
using Om.Default;
using Rt.VTH50;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Threading.Tasks;
using Video.Hik;
using Safover.LidarCollector.Core;
using Safover.LidarCollector.Config;

namespace Safover.LidarCollector.Managers
{
    public class DeviceManager
    {
        private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

        #region Singleton
        public static DeviceManager DeviceFactoryInstance { get; } = new DeviceManager();
        private DeviceManager() { }
        #endregion

        public RotaryTableDevice RotaryTableDevice { get; private set; }
        public DeviceCondition RotaryTableCondition { get; private set; }
        public string RotaryTableConditionDescription { get; private set; }
        public bool IsRotaryTableVirtual { get; private set; }
      
        public AcquisitionCard AcquisitionCard { get; private set; }
        public DeviceCondition AcquisitionCardCondition { get; private set; }
        public string AcquisitionCardConditionDescription { get; private set; }
        public bool IsAcquisitionCardVirtual { get; private set; }



        public OpticalMaserDevice OpticalMaserDevice { get; private set; }
        public DeviceCondition OpticalMaserCondition { get; private set; }
        public string OpticalMaserConditionDescription { get; private set; }
        public bool IsOpticalMaserVirtual { get; private set; }



        public GpsLocatorDevice GpsLocatorDevice { get; private set; }
        public DeviceCondition GpsLocatorCondition { get; private set; }
        public string GpsLocatorConditionDescription { get; private set; }
        public bool IsGpsLocatorVirtual { get; private set; }


        public ElectronicCompass ElectronicCompass { get; private set; }
        public DeviceCondition ElectronicCompassCondition { get; private set; }
        public string ElectronicCompassConditionDescription { get; private set; }
        public bool IsElectronicCompassVirtual { get; private set; }



        public ICamera Camera { get; private set; }
        public string CameraConditionDescription { get; private set; }
        public DeviceCondition CameraCondition { get; private set; }
        public bool IsCameraVirtual { get; private set; }


        public DeviceConditionChangedEventHandler DeviceConditionChangedEvent { get; set; }

        public DeviceReadingsUpdatedEventHandler DeviceReadingsUpdated { get; set; }

        public HashSet<INetworkStatusChangedEventListener> NetworkStatusChangedEventListeners { get; } = new HashSet<INetworkStatusChangedEventListener>();
       

        public void InitializeAll()
        {
            DeviceConditionChangedEvent += OnDeviceConditionChanged;

            InitializeGpsLocator();

            InitializeRotaryTable();

            InitializeOpticalMaser();

            InitializeCamera();

            InitializeAcquisitionCard();

            InitializeElectronicCompass();
        }

        //public void CloseAll() 
        //{
        //    CloseGpsLocator();
        //    CloseRotaryTable();
        //    CloseOpticalMaser();
        //    CloseCamera();
        //    CloseElectronicCompass();

        //    CloseAcquisitionCard();
        //}

        public void CloseAll()
        {
            DeviceConditionChangedEvent -= OnDeviceConditionChanged;

            var tasks = new List<Task>
            {
                Task.Factory.StartNew(() => { CloseAcquisitionCard(); }),

                Task.Factory.StartNew(() => 
                {
                    CloseGpsLocator();
                    CloseRotaryTable();
                    CloseOpticalMaser();
                    CloseCamera();
                    CloseElectronicCompass();
                })
            };

            try
            {
                Task.WaitAll(tasks.ToArray());
            }
            catch (AggregateException e)
            {
                for (int j = 0; j < e.InnerExceptions.Count; j++)
                {
                    var ie = e.InnerExceptions[j];
                    Log.Error($"{ie.Message}", ie);
                }
            }
        }

        public void CloseElectronicCompass()
        {
            if (ElectronicCompass == null)
            {
                return;
            }

            ElectronicCompass.CloseSerialPort();

            ElectronicCompass.SerialPortStatusChanged -= OnElectronicCompassDeviceStatusChanged;
            ElectronicCompass.DeviceReadingsUpdated -= OnDeviceReadingsUpdated;
        }

        public void CloseAcquisitionCard()
        {
            if (AcquisitionCard == null)
            {
                return;
            }

            AcquisitionCard.Disconnect();

            AcquisitionCard.NetworkStatusChanged -= OnNetworkStatusChanged;
            AcquisitionCard.AcquisitionCardStatusChanged -= OnAcquisitionCardStatusChanged;
        }

        public void CloseCamera()
        {
            if (Camera == null || !Camera.IsReady)
            {
                return;
            }

            Camera.CloseCamera();
            Camera.CameraStatusChanged -= OnCameraStatusChanged;
        }

        public void CloseOpticalMaser()
        {
            if (OpticalMaserDevice == null)
            {
                return;
            }

            OpticalMaserDevice.CloseSerialPort();
            OpticalMaserDevice.SerialPortStatusChanged -= OnOpticalMaserDeviceStatusChanged;
            OpticalMaserDevice.DeviceReadingsUpdated -= OnDeviceReadingsUpdated;
        }

        public void CloseRotaryTable()
        {
            if (RotaryTableDevice == null)
            {
                return;
            }

            RotaryTableDevice.CloseSerialPort();
            RotaryTableDevice.SerialPortStatusChanged -= OnRotaryTableDeviceStatusChanged;
            RotaryTableDevice.DeviceReadingsUpdated -= OnDeviceReadingsUpdated;
        }

        public void CloseGpsLocator()
        {
            if (GpsLocatorDevice == null)
            {
                return;
            }

            GpsLocatorDevice.CloseSerialPort();
            GpsLocatorDevice.SerialPortStatusChanged -= OnGpsLocatorDeviceStatusChanged;
            GpsLocatorDevice.DeviceReadingsUpdated -= OnDeviceReadingsUpdated;
        }



        public void InitializeElectronicCompass()
        {
            if (ElectronicCompass != null && ElectronicCompass.IsSerialPortOpen)
            {
                return;
            }

            IsElectronicCompassVirtual = IsVirtualDevice(DeviceType.ElectronicCompass);

            ElectronicCompass = IsElectronicCompassVirtual ? VirtualBewisElectronicCompass.Instance : BewisElectronicCompass.Instance;

            ElectronicCompass.PortName = RuntimeConfigManager.Instance.ElectronicCompassConfig.PortName;

            ElectronicCompass.SerialPortStatusChanged += OnElectronicCompassDeviceStatusChanged;
            ElectronicCompass.DeviceReadingsUpdated += OnDeviceReadingsUpdated;

            ElectronicCompass.KeepOpenSerialPort();
        }

        public void InitializeGpsLocator()
        {
            if (GpsLocatorDevice != null && GpsLocatorDevice.IsSerialPortOpen)
            {
                return;
            }

            IsGpsLocatorVirtual = IsVirtualDevice(DeviceType.GpsLocator);

            GpsLocatorDevice = IsGpsLocatorVirtual ? VirtualNema0183GpsLocationDevice.Instance : Nema0183GpsLocationDevice.Instance;

            var defaultLatitude = RuntimeConfigManager.Instance.OthersConfig.DefaultLatitude;
            var defaultLongitude = RuntimeConfigManager.Instance.OthersConfig.DefaultLongitude;
            var defaultAltitude = RuntimeConfigManager.Instance.OthersConfig.DefaultAltitude;

            SetGpsDefault(defaultLatitude, defaultLongitude, defaultAltitude);

            GpsLocatorDevice.PortName = RuntimeConfigManager.Instance.GPSConfig.PortName;

            GpsLocatorDevice.SerialPortStatusChanged += OnGpsLocatorDeviceStatusChanged;
            GpsLocatorDevice.DeviceReadingsUpdated += OnDeviceReadingsUpdated;

            GpsLocatorDevice.KeepOpenSerialPort();
        }

        public void SetGpsDefault(double defaultLatitude, double defaultLongitude, double defaultAltitude)
        {
            GpsLocatorDevice.DefaultLatitude = defaultLatitude;
            GpsLocatorDevice.DefaultLongitude = defaultLongitude;
            GpsLocatorDevice.DefaultAltitude = defaultAltitude;
        }

        public void InitializeAcquisitionCard()
        {
            if (AcquisitionCard != null && AcquisitionCard.IsConnected)
            {
                return;
            }

            IsAcquisitionCardVirtual = IsVirtualDevice(DeviceType.AcquisitionCard);
            AcquisitionCard = IsAcquisitionCardVirtual ? VirtualBlyAcquisitionCard.Instance : CreateBlyAcquisitionCard();

            AcquisitionCard.NetworkStatusChanged += OnNetworkStatusChanged;
            AcquisitionCard.AcquisitionCardStatusChanged += OnAcquisitionCardStatusChanged;

            AcquisitionCard.KeepConnection();
        }

        public void InitializeOpticalMaser()
        {
            if (OpticalMaserDevice != null && OpticalMaserDevice.IsSerialPortOpen)
            {
                return;
            }

            IsOpticalMaserVirtual = IsVirtualDevice(DeviceType.OpticalMaser);

            OpticalMaserDevice = IsOpticalMaserVirtual ? VirtualNJOpticalMaserDevice.Instance : NJOpticalMaserDevice.Instance;

            OpticalMaserDevice.PortName = RuntimeConfigManager.Instance.OpticalMaserConfig.PortName;

            OpticalMaserDevice.SerialPortStatusChanged += OnOpticalMaserDeviceStatusChanged;
            OpticalMaserDevice.DeviceReadingsUpdated += OnDeviceReadingsUpdated;
            OpticalMaserDevice.KeepOpenSerialPort();
        }

        public void InitializeCamera()
        {
            if (Camera != null && Camera.IsReady)
            {
                return;
            }

            IsCameraVirtual = IsVirtualDevice(DeviceType.Camera);

            Camera = IsCameraVirtual ? new VirtualHikCamera() : (ICamera)new HikCamera();

            Camera.CameraStatusChanged += OnCameraStatusChanged;

            bool cameraOk = Camera.OpenCamera();

            DeviceCondition condition = cameraOk ? DeviceCondition.OK : DeviceCondition.Malfuntion;
            string description = "摄像机正常";

            var args = new CameraConditionChangedEventArgs
            {
                DeviceCondition = condition,
                Description = description,
            };

            DeviceConditionChangedEvent?.Invoke(args);
        }

        public void InitializeRotaryTable()
        {
            if (RotaryTableDevice != null && RotaryTableDevice.IsSerialPortOpen)
            {
                return;
            }

            IsRotaryTableVirtual = IsVirtualDevice(DeviceType.RotaryTable);

            RotaryTableDevice = IsRotaryTableVirtual ? VirtualVTH50RotaryTableDevice.Instance : VTH50RotaryTableDevice.Instance;

            // Incase default portname changed.
            RotaryTableDevice.PortName = RuntimeConfigManager.Instance.RotaryTableConfig.PortName;

            RotaryTableDevice.SerialPortStatusChanged += OnRotaryTableDeviceStatusChanged;
            RotaryTableDevice.DeviceReadingsUpdated += OnDeviceReadingsUpdated;

            RotaryTableDevice.KeepOpenSerialPort();
        }


        private AcquisitionCard CreateBlyAcquisitionCard()
        {
            var ip = RuntimeConfigManager.Instance.AcquisitionCardConfig.HostIP;
            var port = RuntimeConfigManager.Instance.AcquisitionCardConfig.Port;
            Debug.WriteLine($"Bly Acquisition Card initialized: IP=[{ip}] Port=[{port}].");
            return new BlyAcquisitionCard(ip, port);
        }


        private bool IsVirtualDevice(DeviceType deviceType)
        {
            switch (deviceType)
            {
                case DeviceType.RotaryTable:
                    return RuntimeConfigManager.Instance.DeviceConfig.VirtualRotaryTable;
                case DeviceType.OpticalMaser:
                    return RuntimeConfigManager.Instance.DeviceConfig.VirtualOpticalMaser;
                case DeviceType.AcquisitionCard:
                    return RuntimeConfigManager.Instance.DeviceConfig.VirtualAcquisitionCard;
                case DeviceType.Camera:
                    return RuntimeConfigManager.Instance.DeviceConfig.VirtualCamera;
                case DeviceType.GpsLocator:
                    return RuntimeConfigManager.Instance.DeviceConfig.VirtualGpsLocator;
                case DeviceType.ElectronicCompass:
                    return RuntimeConfigManager.Instance.DeviceConfig.VirtualElectronicCompass;
                default:
                    return false;
            }
        }

        #region Device status changed event handlers
        private void OnAcquisitionCardStatusChanged(object sender, AcquisitionCardStatusChangedEventArgs e)
        {
            var args = new AcquisitionCardConditionChangedEventArgs
            {
                DeviceCondition = e.IsAcquisitionCardConnected ? DeviceCondition.OK : DeviceCondition.Unavailable,
                Description = e.IsAcquisitionCardConnected ? "采集卡已连接" : $"采集卡异常：{e.Message}"
            };

            DeviceConditionChangedEvent?.Invoke(args);
        }

        private void OnGpsLocatorDeviceStatusChanged(object sender, SerialPortStatusChangedEventArgs e)
        {
            var args = new GpsLocatorConditionChangedEventArgs
            {
                DeviceCondition = e.IsSerialPortWorkable ? DeviceCondition.OK : DeviceCondition.Unavailable,
                Description = e.IsSerialPortWorkable ? "GPS已连接" : $"GPS异常：{e.Message}"
            };

            DeviceConditionChangedEvent?.Invoke(args);
        }

        private void OnRotaryTableDeviceStatusChanged(object sender, SerialPortStatusChangedEventArgs e)
        {
            var args = new RotaryTableConditionChangedEventArgs
            {
                DeviceCondition = e.IsSerialPortWorkable ? DeviceCondition.OK : DeviceCondition.Unavailable,
                Description = e.IsSerialPortWorkable ? "云台设备已连接" : $"云台设备异常：{e.Message}"
            };

            DeviceConditionChangedEvent?.Invoke(args);
        }

        private void OnOpticalMaserDeviceStatusChanged(object sender, SerialPortStatusChangedEventArgs e)
        {
            var args = new OpticalMaserConditionChangedEventArgs
            {
                DeviceCondition = e.IsSerialPortWorkable ? DeviceCondition.OK : DeviceCondition.Unavailable,
                Description = e.IsSerialPortWorkable ? "激光器已连接" : $"激光器异常：{e.Message}"
            };

            DeviceConditionChangedEvent?.Invoke(args);
        }

        private void OnElectronicCompassDeviceStatusChanged(object sender, SerialPortStatusChangedEventArgs e)
        {
            var args = new ElectronicCompassConditionChangedEventArgs
            {
                DeviceCondition = e.IsSerialPortWorkable ? DeviceCondition.OK : DeviceCondition.Unavailable,
                Description = e.IsSerialPortWorkable ? "电子罗盘已连接" : $"电子罗盘异常：{e.Message}"
            };

            DeviceConditionChangedEvent?.Invoke(args);
        }



        private void OnCameraStatusChanged(object sender, CameraStatusChangedEventArgs e)
        {
            var args = new CameraConditionChangedEventArgs
            {
                DeviceCondition = e.IsCameraConnected ? DeviceCondition.OK : DeviceCondition.Unavailable,
                Description = e.IsCameraConnected ? "摄像机正常" : $"摄像机异常：{e.Message}",
            };

            DeviceConditionChangedEvent?.Invoke(args);
        }

        private void OnDeviceConditionChanged(DeviceConditionChangedEventArgs e)
        {
            switch (e.DeviceType)
            {
                case DeviceType.RotaryTable:
                    {
                        RotaryTableCondition = e.DeviceCondition;
                        RotaryTableConditionDescription = e.Description;
                    }
                    break;
                case DeviceType.OpticalMaser:
                    {
                        OpticalMaserCondition = e.DeviceCondition;
                        OpticalMaserConditionDescription = e.Description;
                    }
                    break;
                case DeviceType.AcquisitionCard:
                    {
                        AcquisitionCardCondition = e.DeviceCondition;
                        AcquisitionCardConditionDescription = e.Description;
                    }
                    break;
                case DeviceType.Camera:
                    {
                        CameraCondition = e.DeviceCondition;
                        CameraConditionDescription = e.Description;
                    }
                    break;
                case DeviceType.GpsLocator:
                    {
                        GpsLocatorCondition = e.DeviceCondition;
                        GpsLocatorConditionDescription = e.Description;
                    }
                    break;
                case DeviceType.ElectronicCompass:
                    {
                        ElectronicCompassCondition = e.DeviceCondition;
                        ElectronicCompassConditionDescription = e.Description;
                    }
                    break;
                default:
                    break;
            }
        }

        private void OnNetworkStatusChanged(object sender, NetworkStatusChangedEventArgs e)
        {
            foreach (var listener in NetworkStatusChangedEventListeners)
            {
                try
                {
                    listener.NotifyNetworkStatusChanged(e.IsNetworkAlive);
                }
                catch (Exception ex)
                {
                    Log.Error($"{listener}", ex);
                }
            }
        }
        #endregion

        #region Report Readings event handler

        private void OnDeviceReadingsUpdated(object sender, DeviceReadingsUpdatedEventArgs args)
        {
            DeviceReadingsUpdated?.Invoke(sender, args);
        }

        #endregion
    }
}
